-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix pmm deadlock #345
base: mainline
Are you sure you want to change the base?
Fix pmm deadlock #345
Conversation
Optimally, we would refill the 4K frames while still holding the paging lock. However, this would need a new interface in pmm to allow refill without taking the paging lock again. |
there is still the deadlock issue where paging wants to get a frame but pmm is waiting for paging to map a new array. It looks like some kind of backoff is required. |
1754bb4
to
aaa42b3
Compare
based on #344 Also fixes a (potential?) bug where |
58ae195
to
6521e20
Compare
6521e20
to
d01825b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am no longer able to reproduce the deadlock 👍
spin_lock(&priority_lock); | ||
|
||
/* if a refill is already active then they are responsible */ | ||
if (!refilling) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe with refilling
variable made atomic, this check can be done before taking the lock?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could do it before and after taking the lock but I don't think it is sufficient to only check before.
Core 0: pmm_lock() -> ... -> refilling = true; unlock(priority) -> ...
Core 1: refill_from_paging() -> if(!refilling) -> ... -> lock(priority)
Here paging would think there is no refilling but there is.
array = vmap_kern_4k(mfn_to_virt_map(frame->mfn), frame->mfn, L1_PROT); | ||
/* switch to special refilling mode to avoid deadlock with paging */ | ||
ASSERT(!refilling); | ||
refilling = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does it need to be handled under the lock below?
|
||
/* switch back to normal mode */ | ||
ASSERT(refilling); | ||
refilling = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same question as above
@@ -117,13 +149,19 @@ static frames_array_t *new_frames_array(void) { | |||
|
|||
init_frames_array(array); | |||
|
|||
total_free_frames += array->meta.free_count; | |||
/* since paging will back off from refilling we are responsible to do it here */ | |||
try_create_4k_frames(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm... why do we need to split frames during a frame array page mapping?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this approach, if paging detects that refilling is already active, it will not recreate the minimum 4k frames itself but delegate it to whoever is already refilling. This avoids double refilling of array frames and also avoids inconsistencies due to two interleaved array refills.
return array; | ||
error: | ||
panic("PMM: Unable to allocate new page for frame array"); | ||
UNREACHABLE(); | ||
} | ||
|
||
static frames_array_t *new_frames_array(void) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: could be inline
|
||
static void try_create_4k_frames(void); | ||
|
||
static void spin_lock_standard() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I would rename it to pmm_lock()
or something like that
void *vmap_frame_refill(void *va, mfn_t mfn, bool paging_lock) { | ||
unsigned long _va = _ul(va) & PAGE_ORDER_TO_MASK(PAGE_ORDER_4K); | ||
|
||
dprintk("%s: va: 0x%p mfn: 0x%lx\n", __func__, va, mfn); | ||
|
||
if (paging_lock) | ||
spin_lock(&vmap_lock); | ||
else | ||
ASSERT(vmap_lock); | ||
|
||
va = _vmap_4k(&cr3, _ptr(_va), mfn, L1_PROT, false); | ||
|
||
if (paging_lock) | ||
spin_unlock(&vmap_lock); | ||
else | ||
ASSERT(vmap_lock); | ||
|
||
return va; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: instead of the paging_lock
check I would create two versions of this function: one with locking and another with the ASSERT (or maybe a BUG_ON). Despite code duplication, it probably will pay off later when debugging something else.
array = vmap_kern_4k(mfn_to_virt_map(frame->mfn), frame->mfn, L1_PROT); | ||
/* switch to special refilling mode to avoid deadlock with paging */ | ||
ASSERT(!refilling); | ||
refilling = true; | ||
|
||
/* only paging will be allowed to take the lock for writing while refilling */ | ||
spin_unlock(&priority_lock); | ||
array = vmap_frame_refill(mfn_to_virt_map(frame->mfn), frame->mfn, from_paging); | ||
spin_lock(&priority_lock); | ||
|
||
/* switch back to normal mode */ | ||
ASSERT(refilling); | ||
refilling = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it might make sense to handle all this code as independent functions (see my comment for the vmap_frame_refill
).
static size_t frames_count[MAX_PAGE_ORDER + 1]; | ||
|
||
static spinlock_t lock = SPINLOCK_INIT; | ||
|
||
static bool refilling; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could be atomic variable, maybe?
An ugly first attempt to allow pmm frame array refill without deadlocking.
Intended to fix #343